<!DOCTYPE html>
<html lang="en">
<!-- 
This IAB MRAID test creative was built by Nathan Carver, currently VP of Engineering at Crisp Media, Inc. on March 26, 2014.
-->
<head>
    <title>Test MRAID resize errors</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

    <!-- full page creative provides its own viewport -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
    
    <!-- uncomment next line to include mraid mock object for browser testing -->
    <!-- script type="text/javascript" src="http://webtester.mraid.org/compliance/assets/mraid.mock.js"></script -->

<!-- -------------------------------------------------------------------------- -->    
<!-- SNIPPET STARTS HERE FOR CUT-AND-PASTE TO USE THIS PAGE AS AN HTML FRAGMENT -->    
<!-- -------------------------------------------------------------------------- -->    
    <!-- use script tag to identify this creative as MRAID -->
    <script type="text/javascript" src="mraid.js" onerror="console.log('mraid.js not found');"></script>

    <!-- styles for creative -->
    <style type="text/css">
    .buttonLabel { /* text on buttons */
        font: 11px arial;
        margin-top: 10px;
    }
    .webdingFont { /* font for arrows */
        font-size: 18px;
        margin-top: 4px;
    }
    .twoLineLabel { /* when text on buttons is expected to wrap */
        margin-top: 4px;
    }
    .instruction { /* text on background giving instructions */
        float:left;
        font: 12px arial;
        margin-left: 4px;
        margin-top: 12px;
        position:relative;
        width:70px;
		font-weight:bold;
    }
    .button { /* button background */
        background-color: white;
        box-shadow: 1px 1px 2px;
		direction:pointer;
        cursor:pointer;
        float:left;
        height: 34px;
        margin-left: 3px;
        margin-right: 3px;
        margin-top: 8px;
        position: relative;
        text-align: center;
        width: 34px;
		border: solid #FF3818 2px;
		border-radius: 6px;
    }
    
    .button:hover{ /* hover only for desktop testing */
        box-shadow: 0 0 0;
    }
    
    #bannerDiv { /* banner size, color, centering */
        background-color:#ffffff;
        height:98px;
        margin-left: auto ;
        margin-right: auto ;
        width:298px;
		border: solid black 1px;
    }
    #resizeDiv { /* enforce new-line for second round of testing */
        clear:left;
    }
	#logoImage {
		background-image: url();
		background-repeat:no-repeat;
		background-position: 2px 0px;
		height:38px;
		width:38px;
	}
	#logo {
		border:none;
		border-radius: 0;
		box-shadow: none;
		cursor:default;
	}
    body { /* in full page mode, remove browser margin */
        margin: 0 0;
    }
    </style>

<script type="text/javascript">
/* logging helpers */

// global variables help identify log output
var DEBUG = 1,
    INFO = 2,
    ERR = 3;

// $log.it is replacement for console.out
// supports mirroring output to window.alert, reducing log output by level
$log = {
    logLevel : 0,                  //set to reduce output, 0 = all
    useAlert : false,              //toggle to also output to window.alert
    preface  : 'logit',            //set to preface all output by $log.it


/**
* $log.it is a replacement for console.out
* @namespace $log
* @method it
* @param {String} msg : message to output to console
* @param {Number} level : log level for this message
*/
    it : function (msg, level) {

        /** @private, toIsoDate converts date/time to ISO format */
        function toIsoDate(d) {
            function pad(n) {
                return n < 10 ? '0' + n : n;
            }
            var s = d.getUTCFullYear() + '-'
                + pad(d.getUTCMonth() + 1) + '-'
                + pad(d.getUTCDate()) + 'T'
                + pad(d.getUTCHours()) + ':'
                + pad(d.getUTCMinutes()) + ':'
                + pad(d.getUTCSeconds()) + 'Z';
            return s;
        }

        /** @private, s is the string value to hold the modified message */
        var s = '';

        // provide a default level if none provided
        if (!level) {
            level = DEBUG;
        }

        // only output message if level exceeds logLevel property
        if (level > $log.logLevel) {
            s = $log.preface + '(' + level + ')-' + toIsoDate(new Date()) + ':' + msg;
            console.log(s);

            // also send message to alert dialogue if useAlert is set
            if ($log.useAlert) {
                alert(s);
            }
        }
    }
};



// $mraid is a wrapper for mraid
// all mraid methods are protected with try/catch blaocks to help report expected errors
// methods are limited to only those used in this tester
$mraid = {

/**
* resize is a wrapper for mraid.resize and also modifies the banner size of this creative
* @namespace $mraid
* @method resize
*/
    resize : function() {
        $log.it('$mraid.resize', DEBUG);
        try {
            // call mraid
            mraid.resize();

            // adjust size of creative based on mraid resize properties
            var el = document.getElementById('bannerDiv'),
                props = mraid.getResizeProperties();

            el.style.width = props.width + 'px';
            el.style.height = props.height + 'px';
            el.style.top = props.offsetY + 'px';
            el.style.left = props.offsetX + 'px';
            $log.it('SUCCESS mraid.resize()', INFO);
        } catch (e) {
            $log.it('ERR calling resize' + ', ' + e.message, ERR);
        }
    },

/**
* getResizeProperties is a wrapper for mraid.getResizeProperties
* @namespace $mraid
* @method getResizeProperties
*/
    getResizeProperties : function () {
        $log.it('$mraid.getResizeProperties', DEBUG);
        try {
            var p = mraid.getResizeProperties();
            $log.it('SUCCESS mraid.getResizeProperties(' + p.width + ',' + p.height + ')', INFO);
            return p;
        } catch (e) {
            $log.it('ERR calling getResizeProperties' + ', ' + e.message, ERR);
        }
    },

/**
* setResizeProperties is a wrapper for mraid.getResizeProperties
* @namespace $mraid
* @method setResizeProperties
*/
    setResizeProperties : function (props) {
        $log.it('$mraid.setResizeProperties', DEBUG);
        try {
            mraid.setResizeProperties(props);
            $log.it('SUCCESS mraid.setResizeProperties()', INFO);
        } catch (e) {
            $log.it('ERR calling setResizeProperties' + ', ' + e.message, ERR);
            return false;
        }
        return true;
    },

/**
* addEventListener is a wrapper for mraid.addEventListener
* @namespace $mraid
* @method addEventListener
*/
    addEventListener : function(evt, listener) {
        $log.it('$mraid.addEventListener', DEBUG);
        try {
            mraid.addEventListener(evt, listener);
            $log.it('SUCCESS mraid.addEventListener()', INFO);
        } catch (e) {
            $log.it('ERR calling addEventListener for ' + evt + ', ' + e.message, ERR);
        }
    },

/**
* getVersion is a wrapper for mraid.getVersion
* @namespace $mraid
* @method getVersion
*/
    getVersion : function() {
        $log.it('$mraid.getVersion', DEBUG);
        try {
            var v = mraid.getVersion();
            $log.it('SUCCESS mraid.getVersion()', INFO);
            return v;
        } catch (e) {
            $log.it('ERR calling getVersion' + ', ' + e.message, ERR);
        }
    },

/**
* getMaxSize is a wrapper for mraid.getMaxSize
* @namespace $mraid
* @method getMaxSize
*/
    getMaxSize : function () {
        $log.it('$mraid.getMaxSize', DEBUG);
        try {
            var o = mraid.getMaxSize();
            $log.it('SUCCESS mraid.getMaxSize(' + o.width + ',' + o.height + ')', INFO);
            return o;
        } catch (e) {
            $log.it('ERR calling getMaxSize' + ', ' + e.message, ERR);
        }
    },

/**
* getState is a wrapper for mraid.getState
* @namespace $mraid
* @method getState
*/
    getState : function () {
        $log.it('$mraid.getState', DEBUG);
        try {
            var s = mraid.getState();
            $log.it('SUCCESS mraid.getState()', INFO);
            return s;
        } catch (e) {
            $log.it('ERR calling getState' + ', ' + e.message, ERR);
        }
    },

/**
* close is a wrapper for mraid.close
* @namespace $mraid
* @method close
*/
    close : function () {
        $log.it('$mraid.close', DEBUG);
        try {
            var s = mraid.close();
            $log.it('SUCCESS mraid.close()', INFO);
            return s;
        } catch (e) {
            $log.it('ERR calling close' + ', ' + e.message, ERR);
        }
    }
};

/////////////////////////////////////////////////////////////////////
// $tester object-class contains all the script to exercise the tests
// it defines the onclick methods which start each test
// use the .init() method to initialize the logging

$tester = {
/**
* init is the public method to start the tester
*   it turns on logging, checks for mraid, and attaches the click event listeners
*
* @param {boolean} isAlertOn set to true to echo all console output to alert dialogs
* @param {Numeric} logLevel use the globals DEBUG, WARN, ERR to limit the console output by excluding that level, 0=ALL
* @param {String} logPreface identify all the log output with this preface string 
* @namespace $tester
* @method init
*/
    init : function(isAlertOn, logLevel, logPreface) {

        //turn on logging
        $log.logLevel = logLevel;
        $log.useAlert = isAlertOn;
        $log.preface = logPreface;
        $log.it('init', DEBUG);

        //confirm that mraid is available
        switch ($mraid.getState()) {
        case 'default':
            //mraid available and ready
            $log.it('MRAID state is ready', INFO);
            break;
        case 'loading':
            //mraid not yet available, register a listener and try again
            $mraid.addEventListener('ready', $tester.init);
            $log.it('MRAID state is not ready, using listener to wait for ready state', INFO);
            return;
        default:
            //mraid state is unknown, failure on version will disable creative
            $log.it('MRAID state is not recognized - tests cannot continue', ERR);
        }

        //setup private variables
        var i,                                                    //counter
            el,                                                   //local DOM element
            clickListener,                                        //local function placeholder
            version = $mraid.getVersion(),                        //local MRAID version
            buttons = document.getElementsByClassName('button');  //DOM collection of all buttons on the creative

        //confirm this MRAID version -- resize is only available after MRAID v2
        if (version === '2.0') {
            //OK - onclick listener will dispatch to testers
            clickListener = $tester.onclickDispatch;
            $mraid.addEventListener('error', $tester.errorListener);
        } else {
            //Version mismatch - onclick listener will disable buttons
            clickListener = $tester.onclickDisable;
            $log.it('ERR version mismatch; cannot run resize tester', ERR);
        }

        //hook up appropriate listener to all buttons on the creative
        for (i = 0; i < buttons.length; i++) {
            el = buttons[i];
            el.addEventListener('click', clickListener, false);
        }
    },

/**
* setDefault is a utility public method to reset the ad creative size, mraid container, and the mraid resize properties
*   it is called by both the error listener and the toggleOffscreen method to reset during testing
* @namespace $tester
* @method setDefault
*/
    setDefault : function() {
        $log.it('setDefault', DEBUG);

        //creative size update
        var el = document.getElementById('bannerDiv');
        el.style.width = '300px';
        el.style.height = '100px';

        //udpate resize properties to match creative size
        $mraid.setResizeProperties({'width' : 300, 'height' : 100, 'offsetX' : 0, 'offsetY' : 0, 'customClosePosition' : 'top-right', 'allowOffscreen' : true});

        //set mraid container back to default state
        if ($mraid.getState === 'resized') {
            $log.it('reset the state back from resize', INFO);
            $mraid.close();
        }
    },


/**
* errorListener is the generic listener for the mraid 'error' event to report info to the console, and reset the ad as needed
* @namespace $tester
* @method errorListener
*/
    errorListener : function(msg, action) {
        //output the error message
        $log.it('ERR: mraid error caught from ' + action + ': ' + msg, ERR);

        //check to reset the state and ad creative back to default
        $tester.setDefault();
    },

/**
* onclickDisable is the public method called when the tester is inactive, it only outputs to console
* @namespace $tester
* @method onclickDisable
*/
    onclickDisable : function() {
        $log.it('mraid resize tester unit disabled', INFO);
    },

/**
* onclickDispatch is the public method called when the tester is active, it dispatches the click to the appropriate tester
*   individual testing methods are private to this function
* @namespace $tester
* @method onclickDispatch
*/
    onclickDispatch : function() {


/**
* @private
* @method mraidTimingErr attempts to call mraid.resize() before any properties are set
* MRAID REFERENCE

Note that resize() relies on parameters that are stored in the resizeProperties JavaScript object. Thus the creative must set those parameters via the setResizeProperties() method BEFORE attempting to resize(). Calling resize() before setResizeProperties will result in an error.

*/
        function mraidTimingErr() {
            $log.it('mraidTimingErr', DEBUG);
            var props = $mraid.getResizeProperties();
            if (props && props.width) {
                $log.it('mraid.setResizeProperties was already called -- this test is now invalid', INFO);
            } else {
                $mraid.resize();
            }
        }


/**
* @private
* @method mraidInvalidErr attempts to call mraid.setResizeProperties() with invalid data
*   the first call uses an incomplete set of properties
*   the second call uses wrong type values
* MRAID REFERENCE

Note that width, height, offsetX and offsetY are required and have no default properties. If the ad creative attempts to call resize() before setting these four properties, the container will leave the ad in its current state and return an error

*/
        function mraidInvalidErr() {
            $log.it('mraidInvalidErr', DEBUG);
            var propsIncomplete = {
                'width' : 320,
                'height' : 100
            },
                propsInvalid = {
                    'width' : 'string',
                    'height' : 'string',
                    'offsetX' : 'string',
                    'offsetY' : 'string',
                    'customClosePosition' : 1,
                    'allowOffscreen' : 'boolean'
                };
            $mraid.setResizeProperties(propsIncomplete);
            $mraid.setResizeProperties(propsInvalid);
        }


/**
* @private
* @method mraidTooSmallErr attempts to call mraid.setResizeProperties() with width and height values that are too small
*   the first call uses an incomplete set of properties
*   the second call uses wrong type values
* MRAID REFERENCE

This requirement also means that a resized ad must be at least 50x50 pixels, to ensure there is room on the resized creative for the close event region.

*/
        function mraidTooSmallErr() {
            $log.it('mraidTooSmallErr', DEBUG);
            var propsTooSmall = {
                'width' : 30,
                'height' : 30,
                'offsetX' : 0,
                'offsetY' : 0,
                'customClosePosition' : 'bottom-right',
                'allowOffscreen' : false
            };
            $mraid.setResizeProperties(propsTooSmall);
        }


/**
* @private
* @method mraidTooBigErr attempts to call mraid.setResizeProperties() with width and height values that are too large
* MRAID REFERENCE

Note: resize should not be used for ad creative that expands to full-screen (or larger) size: for such creative executions expand() should always be used. Resize will always result in a nonmodal size change, and some portion of the app should always remain visible to the end user.

If the parameters are out of range, then the error event identifies the exception.

For example, an error will occur if an ad sets allowOffscreen to "false" but sets the width and height of the resize to be too big to actually fit on the screen.

*/
        function mraidTooBigErr() {
            $log.it('mraidTooBigErr', DEBUG);
            var maxSize = $mraid.getMaxSize(),
                propsTooBig = {
                    'width' : maxSize.width + 10,
                    'height' : maxSize.height + 10,
                    'offsetX' : -5,
                    'offsetY' : -5,
                    'customClosePosition' : 'center',
                    'allowOffscreen' : false
                };
            $mraid.setResizeProperties(propsTooBig);
        }


/**
* @private
* @method mraidRotateErr attempts to resize within the bounds of a portrait screen but that will be out of bounds in landscape
* @TODO: this test is not yet implemented
* MRAID REFERENCE

allowOffscreen cannot solve all positioning issues. For example, if an ad successfully resizes in landscape orientation, but then becomes larger than the size of the screen due to an orientation change to portrait, the setting of allowOffscreen to false will have no effect, as there is no way the container/SDK can successfully reposition a landscape creative to fit on a portrait screen.

*/
        function mraidRotateErr() {
            $log.it('mraidRotateErr', DEBUG);
        }


/**
* @private
* @method mraidToggleOffscreen resets the creative size, mraid container, and toggles the value of allowOffscreen
*   this feature allows you to test the various resize directions and recover after an error
* MRAID REFERENCE

allowOffscreen: (optional) tells the container whether or not it should allow the resized creative to be drawn fully/partially offscreen

*/
        function mraidToggleOffscreen() {
            $log.it('mraidToggleOffscreen', DEBUG);

            //reset the ad creative and mraid container
            $tester.setDefault();

            //toggle the value of the button text and the resizeProperty
            var el = document.getElementById('toggleOffscreenText'),
                label = el.innerHTML,
                props = $mraid.getResizeProperties();
            if (label === 'TRUE') {
                label = 'FALSE';
                props.allowOffscreen = false;
            } else {
                label = 'TRUE';
                props.allowOffscreen = true;
            }

            //set the values for the button text and the resizeProperty
            el.innerHTML = label;
            $mraid.setResizeProperties(props);
        }


/**
* @private
* @method mraidResizeLeft attempts to resize the creative beyond the size of the screen which can cause two errors
*   with allowOffscreen = true, the close indicator location will be off screen and should raise an error
*   with allowOffscrren = false, the size of the creative will be too large and should raise an error
* MRAID REFERENCE

A resized ad must position itself such that the entire close event region appears onscreen. If the container/SDK detects that a request to resize will result in the close event region being offscreen, the container/SDK should return an error, and ignore the resize

*/
        function mraidResizeLeft() {
            $log.it('mraidResizeLeft', DEBUG);
            var maxSize = $mraid.getMaxSize(),
                propsTooFarLeft = {
                    'width' : maxSize.width,
                    'height' : 200,
                    'offsetX' : -100,
                    'offsetY' : 0,
                    'customClosePosition' : 'top-left',
                    'allowOffscreen' : true
                };
            $mraid.setResizeProperties(propsTooFarLeft);
            $mraid.resize();
        }


/**
* @private
* @method mraidResizeRight attempts to resize the creative beyond the size of the screen and with the close indicator off screen
* @see mraidResizeLeft
*/
        function mraidResizeRight() {
            $log.it('mraidResizeRight', DEBUG);

            var maxSize = $mraid.getMaxSize(),
                propsTooFarRight = {
                    'width' : maxSize.width,
                    'height' : 200,
                    'offsetX' : 100,
                    'offsetY' : 0,
                    'customClosePosition' : 'top-right',
                    'allowOffscreen' : true
                };
            $mraid.setResizeProperties(propsTooFarRight);
            $mraid.resize();
        }

/**
* @private
* @method mraidResizeUp attempts to resize the creative beyond the size of the screen and with the close indicator off screen
* @see mraidResizeLeft
*/
        function mraidResizeUp() {
            $log.it('mraidResizeUp', DEBUG);

            var maxSize = $mraid.getMaxSize(),
                propsTooFarUp = {
                    'width' : 300,
                    'height' : maxSize.height,
                    'offsetX' : 0,
                    'offsetY' : -100,
                    'customClosePosition' : 'top-right',
                    'allowOffscreen' : true
                };
            $mraid.setResizeProperties(propsTooFarUp);
            $mraid.resize();
        }

/**
* @private
* @method mraidResizeDown attempts to resize the creative beyond the size of the screen and with the close indicator off screen
* @see mraidResizeLeft
*/
        function mraidResizeDown() {
            $log.it('mraidResizeDown', DEBUG);

            var maxSize = $mraid.getMaxSize(),
                propsTooFarDown = {
                    'width' : 300,
                    'height' : maxSize.height,
                    'offsetX' : 100,
                    'offsetY' : 0,
                    'customClosePosition' : 'bottom-right',
                    'allowOffscreen' : true
                };
            $mraid.setResizeProperties(propsTooFarDown);
            $mraid.resize();
        }

/**
* @private
* @method unknownClick is the default click action for the onclick dispatcher -- execution of this code represents an error in the tester itself
*/
        function unknownClick() {
            $log.it('unknownClick: ' + this.id, ERR);
        }


// Now that the private methods are defined, the dispatch happens with a switch statement
        switch (this.id) {
        case 'timingDiv':
            mraidTimingErr();
            break;
        case 'invalidDiv':
            mraidInvalidErr();
            break;
        case 'tooSmallDiv':
            mraidTooSmallErr();
            break;
        case 'tooBigDiv':
            mraidTooBigErr();
            break;
        case 'rotateDiv':
            mraidRotateErr();
            break;
        case 'toggleOffscreenDiv':
            mraidToggleOffscreen();
            break;
        case 'resizeLeftDiv':
            mraidResizeLeft();
            break;
        case 'resizeRightDiv':
            mraidResizeRight();
            break;
        case 'resizeUpDiv':
            mraidResizeUp();
            break;
        case 'resizeDownDiv':
            mraidResizeDown();
            break;
        default:
            unknownClick();
        }
    }
};

</script>

</head>

<body>
    <!-- container div wraps all labels and buttons -->
    <div id="bannerDiv">
        <!-- instruction for first line -->
        <div id="setResizeDiv" class="instruction">Test properties:</div>
        
        <!-- buttons with labels for first line -->
        <div id="timingDiv" class="button"><div id="timingText" class="buttonLabel twoLineLabel">bad timing</div></div>
        <div id="invalidDiv" class="button"><div id="invalidText" class="buttonLabel twoLineLabel">bad values</div></div>
        <div id="tooSmallDiv" class="button"><div id="tooSmallText" class="buttonLabel twoLineLabel">too<br/>small</div></div>
        <div id="tooBigDiv" class="button"><div id="tooBigText" class="buttonLabel twoLineLabel">too<br/>big</div></div>
        <div id="logo" class="button"><div id="logoImage" class="buttonLabel"></div></div>
        
        <!-- instructions for second line -->
        <div id="resizeDiv" class="instruction">Test offScreen:</div>
        
        <!-- buttons with labels for second line, use entities for arrows -->
        <div id="toggleOffscreenDiv" class="button"><div id="toggleOffscreenText" class="buttonLabel">TRUE</div></div>
        <div id="resizeLeftDiv" class="button"><div id="resizeLeftText" class="buttonLabel webdingFont">&larr;</div></div>
        <div id="resizeRightDiv" class="button"><div id="resizeRightText" class="buttonLabel webdingFont">&rarr;</div></div>
        <div id="resizeUpDiv" class="button"><div id="resizeUpText" class="buttonLabel webdingFont">&uarr;</div></div>
        <div id="resizeDownDiv" class="button"><div id="resizeDownText" class="buttonLabel webdingFont">&darr;</div></div>
    </div>

<script type="text/javascript">
/* initialize manually to simulate ad envrionments where document ready events may have already fired */
        $tester.init(false, 0, 'ResizeErrTester');
</script>    
</body>

<!-- ------------------------------------------------------------------------ -->    
<!-- SNIPPET ENDS HERE FOR CUT-AND-PASTE TO USE THIS PAGE AS AN HTML FRAGMENT -->
<!--    BUT BE SURE TO REMOVE THE </HEAD> <BODY> AND </BODY> TAGS MANUALLY    -->
<!-- ------------------------------------------------------------------------ -->    

</html>